

function get_gtk_ui_root(ui_data)
    if (ui_data:tag() ~= nil) and (ui_data:tag() == "interface") then
        return {ui_data, ui_data, 1}
    end
    return nil
end

function get_gtk_ui_first_child(ui_data, node)
    if node ~= nil then
        local curr_node, parent_node, index = node[1], node[2], node[3]
        if curr_node ~= nil then
            local child_table = curr_node:find(nil)
            if child_table ~= nil then
                return {child_table[1], child_table, 1}
            end
        end
    end
    return nil
end

function get_gtk_ui_next_child(ui_data, node, child)
    if node ~= nil then
        local curr_node, parent_node, index = child[1], child[2], child[3]
        if curr_node ~= nil then
            if (parent_node[index + 1] ~= nil) then
                return {parent_node[index + 1], parent_node, index + 1}
            end
        end
    end
    return nil
end

function get_gtk_ui_has_child(ui_data, node, name)
    if node ~= nil then
        local curr_node, parent_node, index = node[1], node[2], node[3]
        if curr_node ~= nil then
            for k, v in curr_node:children(name) do
                return true
            end
        end
    end
    return false
end

function get_gtk_ui_get_child(ui_data, node, name)
    if node ~= nil then
        local curr_node, parent_node, index = node[1], node[2], node[3]
        if curr_node ~= nil then
            local child_list = {}
            -- 搜索所有子结点加入到一个临时列表中
            for k, v in curr_node:children(name) do
                table.insert(child_list, v)
            end
            -- 如果列表不为空，则返回第一个子结点
            if child_list[1] ~= nil then
                return {child_list[1], child_list, 1}
            end
        end
    end
    return nil
end

function get_gtk_ui_node_name(ui_data, node)
    if node ~= nil then
        if node[1] ~= nil then
            return node[1]:tag()
        end
    end
    return nil
end

function get_gtk_ui_node_value(ui_data, node)
    if node ~= nil then
        if node[1] ~= nil then
            return node[1][1]
        end
    end
    return nil
end

function get_gtk_ui_first_property(ui_data, node)
    if node ~= nil then
        local curr_node, parent_node, index = node[1], node[2], node[3]
        if curr_node ~= nil then
            for key_, val_ in pairs(curr_node) do
                if type(key_) == "string" then  
                    -- print(key_, val_)
                    return {curr_node, {key_, val_}, 1}
                end
            end
            return nil
        end
    end
    return nil
end

function get_gtk_ui_next_property(ui_data, node, attr)
    if attr ~= nil then
        local curr_node, attr_pair, index = attr[1], attr[2], attr[3]
        local next_index = index + 1
        for key_, val_ in pairs(curr_node) do
            if type(key_) == "string" then  
                if index == 0 then
                    -- print(key_, val_)
                    return {curr_node, {key_, val_}, next_index}
                end
                index = index - 1
            end
        end
    end
    return nil
end

function get_gtk_ui_has_property(ui_data, node, name)
    if node ~= nil then
        local curr_node, parent_node, index = node[1], node[2], node[3]
        if curr_node ~= nil then
            for key_, val_ in pairs(curr_node) do
                if (type(key_) == "string") then  
                    if (key_ == name) then
                        return true
                    end
                end
            end
            return false
        end
    end
    return false
end

function get_gtk_ui_get_property(ui_data, node, name)
    if node ~= nil then
        local curr_node, parent_node, index = node[1], node[2], node[3]
        if curr_node ~= nil then
            for key_, val_ in pairs(curr_node) do
                if type(key_) == "string" then  
                    if (key_ == name) then
                        return {curr_node, {key_, val_}, 1}
                    end
                end
            end
            return nil
        end
    end
    return nil
end

function get_gtk_ui_property_name(ui_data, node, attr)
    if attr ~= nil then
        if attr[2] ~= nil then
            return attr[2][1]
        end
    end
    return nil
end

function get_gtk_ui_property_value(ui_data, node, attr)
    if attr ~= nil then
        if attr[2] ~= nil then
            return attr[2][2]
        end
    end
    return nil
end

function check_file_exists(file_name)
    local file_handle = io.open(file_name, "r+")
	if file_handle ~= nil then
		io.close(file_handle) 
		return true
	else
		return false
	end
end

local function exists(name)
    if type(name)~="string" then
        return false
    end
    local ok, err, code = os.rename(name, name)
    if not ok then
       if code == 13 then
          -- Permission denied, but it exists
          return true
       end
    end
    return ok
end

function split_pathinfo(name)
    return {string.match(name, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")};
end

function split_pathinfo2(name)
    return {string.match(name, "(.-)(([^\\/]-)%.?([^%.\\/]*))")};
end

function check_folder_exists(name)
    if lfs ~= nil then
        if type(name)~="string" then return false end
        local cd = lfs.currentdir()
        local is = lfs.chdir(name) and true or false
        lfs.chdir(cd)
        return is
    end
    return (exists(name) and not check_file_exists(name))
end

function table.copy_to_left(t1, t2)
    -- 空表，直接返回，不操作
    if t2 == nil then
        return
    end
    for k, v in pairs(t2) do
        if (type(v) == "table") and (type(t1[k] or false) == "table") then
            if type(k) == "number" then
                local temp = {}
                table.copy_to_left(temp, t2[k])
                table.insert(t1, temp)
            else
                table.copy_to_left(t1[k], t2[k])
            end
        else
            if type(k) == "number" then
                table.insert(t1, v)
            else
                t1[k] = v
            end
        end
    end
end

-- 从Widget节点中获取ID、class
local function get_gtk_widget_class_and_id(ui_data, widget_node, id_name, class_name)
	-- 至少两个名字
	if (type(id_name) ~= "string" and class_name ~= "string") then
		return nil;
	end
	-- 获取class属性
	local class_node = get_gtk_ui_get_property(ui_data, widget_node, class_name);
	-- class属性必须存在
	if (class_node == nil) then
		print ("[ERROR] BAD Widget miss ".. class_name);
		return nil;
	end
	-- 类名
	local class_name = get_gtk_ui_property_value(ui_data, widget_node, class_node);
	-- 如果有ID属性
	local id_node = get_gtk_ui_get_property(ui_data, widget_node, id_name);
	-- 判断是否为空
	if (id_node ~= nil) then
		return class_name, get_gtk_ui_property_value(ui_data, widget_node, id_node);
	else
		-- 没有ID属性，尝试获取class属性
		local class_node = get_gtk_ui_get_property(ui_data, widget_node, class_name);
		-- 不为空，拼接
		if (class_node ~= nil) then
			-- 返回类名，ID使用类名加一个随机数
			return class_name, class_name .. "_Unnamed_" .. tostring(Gtkui.gen_gtk_widget_id(class_name));
		else
			print ("[WARN] Widget miss ".. id_name);
		end
	end
	return nil;
end

function get_ui_top_widget_list(xml, file_name)
    print("scan ui file: " .. file_name)
    local ui_data = xml.load(file_name)
    -- 取得根结点 
    local root_node = get_gtk_ui_root(ui_data);
    -- 非空再生成
    if root_node ~= nil then
        -- 取得第一个子结点
        local child_node = get_gtk_ui_first_child(ui_data, root_node);

        -- 保存格式化的控件数据
        local widget_list = {};
        -- 循环处理每一个子结点
        while child_node ~= nil do
            -- 取得结点名称
            local object_name = get_gtk_ui_node_name(ui_data, child_node);
            -- 校验
            if object_name ~= nil then
                -- Top控件
                if object_name == "object" then
                    -- 获取控件ID
                    local class_name, id_value = get_gtk_widget_class_and_id(ui_data, child_node, "id", "class");
                    table.insert(widget_list, {class = class_name, id = id_value, is_template = false});
                elseif object_name == "template" then
                    -- 模板的话，需要特殊处理
                    local class_name, id_value = get_gtk_widget_class_and_id(ui_data, child_node, "class", "parent");
                    table.insert(widget_list, {parent = class_name, class = id_value, is_template = true});
                else
                    print ("[WARN] Skip Top Widget : " .. object_name);
                end
            else
                print ("[WARN] Bad Top Widget, without name!");
            end
            -- 取得下一子结点
            child_node = get_gtk_ui_next_child(ui_data, root_node, child_node);
        end
        return widget_list;
    end
    return {}
end

function scan_template_ui_list(ui_folder)
    local ui_templ_data = {}

    -- 测试代码则以luaXML方式加载相应的UI文件
    local xml = require("LuaXML")
    -- 扫描文件夹下的所有UI文件，依次加载并解析
    for file in lfs.dir(ui_folder) do
        -- 忽略当前目录和上级目录
        if file ~= "." and file ~= ".." then
            -- 文件路径
            local file_path = ui_folder .. "/" .. file
            -- 判断是否为文件
            if lfs.attributes(file_path, "mode") == "file" then
                -- 判断文件名是否合法
                local file_name = string.match(file, "^(.*)%.ui$")
                if file_name ~= nil then
                    table.copy_to_left(ui_templ_data, get_ui_top_widget_list(xml, file_path))
                end
            end
        end
    end
    return ui_templ_data
end

function newpairs(t)
    local a = {}
    for n in pairs(t) do a[#a + 1] = n end
    table.sort(a)
    local i = 0
    return function ()
        i = i + 1
        return a[i], t[a[i]]
    end
end

function write_ui_template_data(ui_ver, ui_tmpl_data, dest_file)
    local function get_template_class_info(ui_ver, class_name)
        return string.format(
[[
%s.GtkTmplWidget%s = {
    class = "GtkTmplWidget%s",
    constructor = {
        -- 名称[- ==> _], 
        new = { gencode = "TMPL_WIDGET({* id *})", 
                params = { type = {}, name = {} } },
    },
    signal = {
        -- 信号名称[- ==> _], 信号返回值, 信号参数列表
    },
    property = {
        -- 属性名称  属性类型 缺省值 代码生成
        __parent_class__ = {__type__="GtkWidget*" ,  __default__ = "NULL", __gencode__ = "", tmpl_depend = true, macro_code = {uisources = "TMPL_WIDGET"}},
    },
    parent = %s.%s,
};

]], ui_ver, class_name, class_name, ui_ver, class_name)
    end
    
    -- 打开文件，依次写入代码
    local file = io.open(dest_file, "w")
    if file ~= nil then
        local tmpl_class_list = {}
        file:write("Gtkui = Gtkui or {};\n\nGtkui.ProjectIdList = {\n")
        for _, ui_tmpl in ipairs(ui_tmpl_data) do
            if ui_tmpl.is_template then
                file:write(string.format("    {class = \"%s\", parent = \"%s\", is_template = true},\n", ui_tmpl.class, ui_tmpl.parent))
                -- 如果模板类中没有这个，则添加一项
                if tmpl_class_list[ui_tmpl.parent] == nil then
                    tmpl_class_list[ui_tmpl.parent] = ui_tmpl.parent
                end
            else
                file:write(string.format("    {id = \"%s\", class = \"%s\", is_template = false},\n", ui_tmpl.id, ui_tmpl.class))
            end
        end
        file:write("};\n\n")
        -- 再添加需要的几个模板类
        for _, class_name in newpairs(tmpl_class_list) do
            file:write(get_template_class_info(ui_ver, class_name))
        end

        file:close()
    end
end

-- 获取当前脚本的路径
local curr_path = string.match(string.gsub(debug.getinfo(1,"S").short_src, "\\", "/"), "^.*/")
-- 将当前脚本当前目录加入package搜索列表中
package.path = string.format("%s?.lua;%s?/init.lua;%s", curr_path, curr_path, package.path)
print("package.path: " .. package.path)

-- 添加调试 
--require("socket.core")
--require("LuaPanda").start("127.0.0.1", 8818)
require("lfs")
print("*****************************************************")

local ui_folder  = arg[1]
local dest_file  = arg[2]

-- 默认生成Gtk224版本的代码
local gtk_ver = "Gtk224";

-- 扫描剩余参数，并设置相关变量
for i = 3, #arg do
	local param = arg[i]
	if string.find(param, "^%-%-gtk%-ver=") then
		gtk_ver = string.sub(param, 11)
	end
end

if not check_folder_exists(ui_folder) then
    print("ui folder not exist: " .. ui_folder)
    return
end

if check_file_exists(dest_file) then
	print("dest file exist, will overwrite it, " .. dest_file)
end

-- UI文件中各个实例的ID列表
local ui_templ_data = scan_template_ui_list(ui_folder)

-- 输出到文件
if ui_templ_data ~= nil then
    write_ui_template_data(gtk_ver, ui_templ_data, dest_file)
end
